package org.cybzacg.convert.converters.date;

import lombok.extern.slf4j.Slf4j;
import org.cybzacg.convert.context.ConversionContext;
import org.cybzacg.convert.core.AbstractTypeConverter;
import org.cybzacg.convert.enums.ConversionError;
import org.cybzacg.convert.enums.ConversionPriority;
import org.cybzacg.convert.exception.ConversionException;

import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;

/**
 * 日期时间转换器抽象基类
 * 提取日期时间转换器的公共逻辑
 *
 * <p>
 * 功能特性：
 * <ul>
 * <li>DateTimeFormatter 缓存管理（LRU缓存，最大1000条）</li>
 * <li>统一的字符串解析逻辑</li>
 * <li>通用的时间戳转换</li>
 * <li>标准的 Date/Instant 转换</li>
 * </ul>
 *
 * <p>
 * 使用示例：
 * 
 * <pre>
 * {
 *     &#64;code
 *     public class LocalDateConverter extends AbstractDateTimeConverter<LocalDate> {
 *         public LocalDateConverter() {
 *             super(LocalDate.class, ConversionPriority.MEDIUM_HIGH);
 *         }
 *
 *         &#64;Override
 *         protected LocalDate parseFromString(String str, DateTimeFormatter formatter) {
 *             return LocalDate.parse(str, formatter);
 *         }
 *
 *         @Override
 *         protected String[] getSupportedPatterns() {
 *             return DateFormats.DATE_FORMAT_PATTERNS;
 *         }
 *     }
 * }
 * </pre>
 *
 * @param <T> 目标日期时间类型
 * @author cybzacg.blog
 * @version 4.0
 * @since 4.0
 */
@Slf4j
public abstract class AbstractDateTimeConverter<T> extends AbstractTypeConverter {

    /**
     * 最大缓存大小，防止内存泄漏
     */
    private static final int MAX_FORMATTER_CACHE_SIZE = 1000;

    /**
     * DateTimeFormatter LRU缓存
     * 避免重复创建 formatter 对象，同时防止缓存无限增长
     */
    private static final Map<String, DateTimeFormatter> FORMATTER_CACHE = Collections.synchronizedMap(
            new LinkedHashMap<String, DateTimeFormatter>(MAX_FORMATTER_CACHE_SIZE + 1, 0.75f, true) {
                @Override
                protected boolean removeEldestEntry(Map.Entry<String, DateTimeFormatter> eldest) {
                    return size() > MAX_FORMATTER_CACHE_SIZE;
                }
            });

    /**
     * 目标日期时间类型
     */
    private final Class<T> targetDateTimeType;

    /**
     * 默认时区
     */
    protected static final ZoneId DEFAULT_ZONE_ID = ZoneId.systemDefault();

    /**
     * 构造函数
     *
     * @param targetDateTimeType 目标日期时间类型
     * @param priority           转换优先级
     */
    protected AbstractDateTimeConverter(Class<T> targetDateTimeType, ConversionPriority priority) {
        super(priority);
        this.targetDateTimeType = Objects.requireNonNull(targetDateTimeType, "Target datetime type cannot be null");
        log.debug("{} 初始化完成，目标类型: {}, 优先级: {}",
                getClass().getSimpleName(), targetDateTimeType.getSimpleName(), priority);
    }

    @Override
    @SuppressWarnings("unchecked")
    protected <R> R doConvert(Object source, Class<R> targetType, R defaultValue, ConversionContext context)
            throws Exception {

        log.debug("开始{}转换: 源类型={}, 目标类型={}, 默认值={}",
                targetDateTimeType.getSimpleName(),
                source != null ? source.getClass().getSimpleName() : "null",
                targetType.getSimpleName(),
                defaultValue);

        if (source == null) {
            R result = defaultValue != null ? defaultValue : (R) getDefaultValue();
            log.debug("源对象为null，返回默认值: {}", result);
            return result;
        }

        if (targetType.isInstance(source)) {
            log.debug("源对象已经是目标类型，直接返回: {}", source);
            return (R) source;
        }

        try {
            T result = convertToDateTime(source, context);
            if (result == null) {
                R fallbackResult = defaultValue != null ? defaultValue : (R) getDefaultValue();
                log.debug("转换为{}失败，返回默认值: {}", targetDateTimeType.getSimpleName(), fallbackResult);
                return fallbackResult;
            }

            log.debug("{}转换完成: {} -> {}", targetDateTimeType.getSimpleName(), source, result);
            return (R) result;
        } catch (Exception e) {
            log.error("{}转换失败: {} -> {}", targetDateTimeType.getSimpleName(), source, targetType, e);
            throw new ConversionException(ConversionError.CONVERSION_FAILED,
                    String.format("%s转换失败: %s -> %s", targetDateTimeType.getSimpleName(), source, targetType), e);
        }
    }

    /**
     * 将对象转换为目标日期时间类型
     *
     * @param source  源对象
     * @param context 转换上下文
     * @return 转换后的日期时间对象
     * @throws ConversionException 转换失败时抛出
     */
    protected T convertToDateTime(Object source, ConversionContext context) throws ConversionException {
        if (source == null) {
            return null;
        }

        // 如果已经是目标类型，直接返回
        if (targetDateTimeType.isInstance(source)) {
            return targetDateTimeType.cast(source);
        }

        // 字符串转换
        if (source instanceof String str) {
            return convertFromString(str, context);
        }

        // 数字转换（时间戳）
        if (source instanceof Number number) {
            return convertFromNumber(number, context);
        }

        // Date转换
        if (source instanceof Date date) {
            return convertFromDate(date, context);
        }

        // Instant转换
        if (source instanceof java.time.Instant instant) {
            return convertFromInstant(instant, context);
        }

        // 其他日期时间类型转换（由子类实现）
        T result = convertFromOtherDateTime(source, context);
        if (result != null) {
            return result;
        }

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                String.format("无法将类型 %s 转换为 %s",
                        source.getClass().getSimpleName(), targetDateTimeType.getSimpleName()));
    }

    /**
     * 从字符串转换
     *
     * @param source  源字符串
     * @param context 转换上下文
     * @return 转换结果
     * @throws ConversionException 转换失败时抛出
     */
    protected T convertFromString(String source, ConversionContext context) throws ConversionException {
        String str = source.trim();
        if (str.isEmpty()) {
            return null;
        }

        String[] patterns = getSupportedPatterns();
        if (patterns == null || patterns.length == 0) {
            throw new ConversionException(ConversionError.CONFIGURATION_ERROR,
                    "未配置支持的日期格式模式");
        }

        for (String pattern : patterns) {
            try {
                DateTimeFormatter formatter = getFormatter(pattern, context);
                return parseFromString(str, formatter);
            } catch (DateTimeParseException e) {
                continue;
            }
        }

        throw new ConversionException(ConversionError.FORMAT_ERROR,
                String.format("无法将字符串 '%s' 转换为 %s", str, targetDateTimeType.getSimpleName()));
    }

    /**
     * 从数字转换（时间戳）
     *
     * @param source  源数字
     * @param context 转换上下文
     * @return 转换结果
     */
    protected T convertFromNumber(Number source, ConversionContext context) {
        long milliseconds = source.longValue();
        java.time.Instant instant = java.time.Instant.ofEpochMilli(milliseconds);
        return convertFromInstant(instant, context);
    }

    /**
     * 从 Date 转换
     *
     * @param source  源 Date 对象
     * @param context 转换上下文
     * @return 转换结果
     */
    protected T convertFromDate(Date source, ConversionContext context) {
        return convertFromInstant(source.toInstant(), context);
    }

    /**
     * 从字符串解析为目标类型（抽象方法，由子类实现）
     *
     * @param str       源字符串
     * @param formatter 日期时间格式化器
     * @return 解析结果
     * @throws DateTimeParseException 解析失败时抛出
     */
    protected abstract T parseFromString(String str, DateTimeFormatter formatter) throws DateTimeParseException;

    /**
     * 从 Instant 转换（抽象方法，由子类实现）
     *
     * @param instant Instant 对象
     * @param context 转换上下文
     * @return 转换结果
     */
    protected abstract T convertFromInstant(java.time.Instant instant, ConversionContext context);

    /**
     * 获取支持的日期格式模式（抽象方法，由子类实现）
     *
     * @return 日期格式模式数组
     */
    protected abstract String[] getSupportedPatterns();

    /**
     * 从其他日期时间类型转换（钩子方法，子类可选实现）
     *
     * @param source  源对象
     * @param context 转换上下文
     * @return 转换结果，如果不支持返回 null
     */
    protected T convertFromOtherDateTime(Object source, ConversionContext context) {
        return null;
    }

    /**
     * 获取默认值（钩子方法，子类可选实现）
     *
     * @return 默认值
     */
    protected T getDefaultValue() {
        return null;
    }

    /**
     * 获取 DateTimeFormatter（带缓存）
     *
     * @param pattern 日期格式模式
     * @param context 转换上下文
     * @return DateTimeFormatter 对象
     */
    protected DateTimeFormatter getFormatter(String pattern, ConversionContext context) {
        return FORMATTER_CACHE.computeIfAbsent(pattern, DateTimeFormatter::ofPattern);
    }

    /**
     * 清空 formatter 缓存（用于测试）
     */
    protected static void clearFormatterCache() {
        FORMATTER_CACHE.clear();
    }

    @Override
    public Class<?> getSupportedType() {
        return targetDateTimeType;
    }

    @Override
    public boolean supports(Class<?> targetType) {
        return targetType == targetDateTimeType;
    }
}
